package com.hex.ds.hdrs.engine.manager;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.hex.ds.hdrs.common.constant.Const;
import com.hex.ds.hdrs.common.constant.ErrorCode;
import com.hex.ds.hdrs.common.exception.BizException;
import com.hex.ds.hdrs.common.helper.MapHelper;
import com.hex.ds.hdrs.common.utils.ListUtil;
import com.hex.ds.hdrs.engine.service.LstFinishService;
import com.hex.ds.hdrs.period.job.po.PeriodJob;
import com.hex.ds.hdrs.period.task.po.PeriodTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @Package: com.hex.ds.hdrs.engine.manager
 * @ClassName LstFinishManager
 * @Description: 最新完成队列管理
 * @Author: jd.chen
 * @Date 2021/7/5 17:54
 * @Version v1.0
 **/
@Slf4j
@Component
public class LstFinishManager {

    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwl.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();

    /**
     * latestJobMap 同步锁
     */
    private final byte[] jobMapSyn = {};
    /**
     * latestTaskMap 同步锁
     */
    private final byte[] taskMapSyn = {};
    /**
     * deltaJobList 同步锁
     */
    private final byte[] deltaJobSyn = {};
    /**
     * deltaTaskList 同步锁
     */
    private final byte[] deltaTaskSyn = {};

    /**
     * 打标
     */
    private String mark;
    /**
     * 上次查询时间
     */
    private String lastQryTs;
    /**
     * 本次查询时间
     */
    private String curQryTs;
    /**
     * 最新完成作业map<jobCode,job>
     */
    private Map<String, PeriodJob> latestJobMap = new ConcurrentHashMap<>();
    /**
     * 最新完成任务map<taskCode,task>
     */
    private Map<String, PeriodTask> latestTaskMap = new ConcurrentHashMap<>();
    /**
     * 作业的delta集合
     */
    private List<PeriodJob> deltaJobList = Collections.synchronizedList(new ArrayList<>());
    /**
     * 任务的delta集合
     */
    private List<PeriodTask> deltaTaskList = Collections.synchronizedList(new ArrayList<>());

    @Value("${logging.showMemoryList}")
    private boolean showMemoryList;

    @Autowired
    private LstFinishService lstFinishService;

    /**
     * 清空内存
     */
    public void clear() {
        this.latestJobMap.clear();
        this.latestTaskMap.clear();
        this.mark = Const.STOP_LST_FNS_MARK;
        this.deltaJobList.clear();
        this.deltaTaskList.clear();
    }

    /**
     * 清空增量delta集合
     */
    public void clearDeltaJobList() {
        this.deltaJobList.clear();
    }

    /**
     * 初始化
     */
    public void init() {
        log.debug("最新完成队列--初始化【LstFinishManager.init】开始==========》");
        if (!lstFinishService.isCanDeal()) {
            log.warn("【该中心不是Master】");
            return;
        }
        try {
            //1. 打标
            this.setMark(Const.INIT_LST_FNS_MARK);
            //2. 设置本次查询时间
            this.setCurQryTs(DateUtil.now());
            //3. 获取jobMap
            Map<String, PeriodJob> lstJobMap = this.lstFinishService.qryLstFnsJobListMap();
            //4. 获取taskMap
            Map<String, PeriodTask> lstTaskMap = this.lstFinishService.qryLstFnsTaskListMap();
            //5. 内存化最新完成队列
            this.init(lstJobMap, lstTaskMap);
        } catch (Exception e) {
            log.error("最新完成队列--初始化【LstFinishManager.init】=====异常: {}" , e.getMessage());
            throw new BizException(ErrorCode.ERROR_810008);
        } finally {
            this.setMark(Const.FREE_LST_FNS_MARK);
            log.debug("最新完成队列--初始化【LstFinishManager.init】结束《=======");
        }
        log.debug("【LstFinishManager.init】结束《==========");
    }

    /**
     * 初始化
     *
     * @param latestJobMap
     * @param latestTaskMap
     */
    public void init(Map<String, PeriodJob> latestJobMap, Map<String, PeriodTask> latestTaskMap) {
        log.info("初始化处理【LatestFinishManager.init】=======》");
        this.latestJobMap = latestJobMap;
        this.latestTaskMap = latestTaskMap;
        this.mark = Const.FREE_LST_FNS_MARK;
        this.lastQryTs = this.curQryTs;
    }

    /**
     * @Method: getAllJobMap
     * @Param: []
     * @Return: java.util.Map<java.lang.String,com.hex.ds.hdrs.period.job.po.PeriodJob>
     * @Description： 获取全量作业map
     * @Author： jd.chen
     * @Date： 2021/7/5 18:26
     * @Version： V1.0
     */
    public Map<String, PeriodJob> getAllJobMap() {
        log.debug("获取增量作业map【LstFinishManager.getAddJobMap】开始=======》");
        //1. 设置本次查询时间
        this.setCurQryTs(DateUtil.now());
        return lstFinishService.getAllJobMap();
    }

    /**
     * @Method: getAllTaskMap
     * @Param: []
     * @Return: java.util.Map<java.lang.String,com.hex.ds.hdrs.period.task.po.PeriodTask>
     * @Description： 获取全量任务map
     * @Author： jd.chen
     * @Date： 2021/7/5 18:35
     * @Version： V1.0
     */
    public Map<String, PeriodTask> getAllTaskMap() {
        log.info("获取增量任务map【LstFinishManager.getAddTaskMap】开始=======》");
        //1. 设置本次查询时间
        this.setCurQryTs(DateUtil.now());
        return lstFinishService.getAllTaskMap();
    }

    /**
     * @Method: getAddJobList
     * @Param: []
     * @Return: java.util.List<com.hex.ds.hdrs.period.job.po.PeriodJob>
     * @Description： 获取全量任务map
     * @Author： jd.chen
     * @Date： 2021/7/5 18:36
     * @Version： V1.0
     */
    public List<PeriodJob> getAddJobList() {
        log.debug("获取增量作业map【LstFinishManager.getAddJobList】开始=======》");
        //1. 设置本次查询时间
        this.setCurQryTs(DateUtil.now());
        return lstFinishService.getAddJobList(lastQryTs);
    }

    /**
     * @Method: getAddTaskList
     * @Param: []
     * @Return: java.util.List<com.hex.ds.hdrs.period.task.po.PeriodTask>
     * @Description： 获取全量任务map
     * @Author： jd.chen
     * @Date： 2021/7/5 18:36
     * @Version： V1.0
     */
    public List<PeriodTask> getAddTaskList() {
        log.debug("获取增量任务map【LstFinishManager.getAddTaskList】开始=======》");
        //1. 设置本次查询时间
        this.setCurQryTs(DateUtil.now());
        return lstFinishService.getAddTaskList(lastQryTs);
    }

    /**
     * @Method: mergeLstJob
     * @Param: [srcJobList]
     * @Return: void
     * @Description： 合并最新完成作业map
     * @Author： jd.chen
     * @Date： 2021/7/5 18:36
     * @Version： V1.0
     */
    public void mergeLstJob(List<PeriodJob> srcJobList) {
        log.debug("合并最新完成作业map【LstFinishManager.mergeLstJob】开始=======》");
        for (PeriodJob srcJob : srcJobList) {
            String srcJobCode = srcJob.getJobCode();
            if (!this.isContainsJob(srcJobCode)) {
                //内存中不存在作业，直接添加
                this.addJob(srcJob);
                continue;
            }

            PeriodJob targetJob = getJob(srcJobCode);
            this.mergeJob(srcJob, targetJob);
        }
    }

    /**
     * @Method: mergeJob
     * @Param: [srcJob, targetJob]
     * @Return: void
     * @Description： 合并任务
     * @Author： jd.chen
     * @Date： 2021/7/5 18:36
     * @Version： V1.0
     */
    private void mergeJob(PeriodJob srcJob, PeriodJob targetJob) {
        log.debug("合并作业【LstFinishManager.mergeJob】开始=======》");
        if (DateUtil.parse(srcJob.getUpTs()).after(DateUtil.parse(targetJob.getUpTs()))) {
            targetJob.setJobStatus(srcJob.getJobStatus());
            targetJob.setUpTs(srcJob.getUpTs());
        }
    }

    /**
     * @Method: mergeLstTask
     * @Param: [srcTaskList]
     * @Return: void
     * @Description： 合并最新完成任务map
     * @Author： jd.chen
     * @Date： 2021/7/5 18:36
     * @Version： V1.0
     */
    public void mergeLstTask(List<PeriodTask> srcTaskList) {
        log.debug("合并最新完成任务map【LstFinishManager.mergeLstTask】开始=======》");
        for (PeriodTask srcTask : srcTaskList) {
            String srcTaskCode = srcTask.getTaskCode();
            if (!this.isContainsTask(srcTaskCode)) {
                this.addTask(srcTask);
                continue;
            }

            PeriodTask targetTask = getTask(srcTaskCode);
            this.mergeTask(srcTask, targetTask);
        }
    }

    /**
     * @Method: mergeTask
     * @Param: [srcTask, targetTask]
     * @Return: void
     * @Description： 合并任务
     * @Author： jd.chen
     * @Date： 2021/7/5 18:36
     * @Version： V1.0
     */
    private void mergeTask(PeriodTask srcTask, PeriodTask targetTask) {
        log.debug("合并任务【LstFinishManager.mergeTask】开始=======》");
        if (DateUtil.parse(srcTask.getUpTs()).after(DateUtil.parse(targetTask.getUpTs()))) {
            targetTask.setTaskStatus(srcTask.getTaskStatus());
            targetTask.setUpTs(srcTask.getUpTs());
        }
    }

    /**
     * @Method: mergeDelta
     * @Param: []
     * @Return: void
     * @Description： 合并delta
     * @Author： jd.chen
     * @Date： 2021/7/5 18:36
     * @Version： V1.0
     */
    public void mergeDelta() {
        log.info("合并任务【LstFinishManager.mergeDelta】开始=======》");
        //1. 合并delta
        this.mergeDeltaLstJob();
        this.mergeDeltaLstTask();
        //2. 清空delta
        this.clearDelta();
    }

    /**
     * @Method: mergeDeltaLstJob
     * @Param: []
     * @Return: void
     * @Description： 合并最新完成作业map
     * @Author： jd.chen
     * @Date： 2021/7/5 18:36
     * @Version： V1.0
     */
    public void mergeDeltaLstJob() {
        log.debug("合并最新完成作业map【LstFinishManager.mergeDeltaLstJob】开始=======》");
        if (!ListUtil.isEmpty(deltaJobList)) {
            synchronized (deltaJobSyn) {
                for (int i = 0; i < deltaJobList.size(); i++) {
                    PeriodJob job = deltaJobList.get(i);
                    String srcJobCode = job.getJobCode();
                    if (!this.isContainsJob(srcJobCode)) {
                        //内存中不存在作业，直接添加
                        this.addJob(job);
                        continue;
                    }

                    PeriodJob targetJob = getJob(srcJobCode);
                    this.mergeJob(job, targetJob);
                }
            }
        }
    }

    /**
     * @Method: mergeDeltaLstTask
     * @Param: []
     * @Return: void
     * @Description： 合并最新完成任务map
     * @Author： jd.chen
     * @Date： 2021/7/5 18:36
     * @Version： V1.0
     */
    public void mergeDeltaLstTask() {
        log.debug("合并最新完成任务map【LstFinishManager.mergeDeltaLstTask】开始=======》");
        if (!ListUtil.isEmpty(deltaTaskList)) {
            synchronized (deltaTaskSyn) {
                for (int i = 0; i < deltaTaskList.size(); i++) {
                    PeriodTask task = deltaTaskList.get(i);
                    String srcTaskCode = task.getTaskCode();
                    if (!this.isContainsTask(srcTaskCode)) {
                        this.addTask(task);
                        continue;
                    }

                    PeriodTask targetTask = getTask(srcTaskCode);
                    this.mergeTask(task, targetTask);
                }
            }
        }
    }

    /**
     * @Method: getTask
     * @Param: [taskCode]
     * @Return: com.hex.ds.hdrs.period.task.po.PeriodTask
     * @Description： 获取任务
     * @Author： jd.chen
     * @Date： 2021/7/5 18:37
     * @Version： V1.0
     */
    public PeriodTask getTask(String taskCode) {
        synchronized (taskMapSyn) {
            return latestTaskMap.get(taskCode);
        }
    }

    /**
     * @Method: getJob
     * @Param: [jobCode]
     * @Return: com.hex.ds.hdrs.period.job.po.PeriodJob
     * @Description： 获取作业
     * @Author： jd.chen
     * @Date： 2021/7/5 18:37
     * @Version： V1.0
     */
    public PeriodJob getJob(String jobCode) {
        synchronized (jobMapSyn){
            return latestJobMap.get(jobCode);
        }
    }

    /**
     * Delta增加作业
     *
     * @param job
     */
    public void addDeltaJob(PeriodJob job) {
        this.deltaJobList.add(job);
    }

    /**
     * Delta增加任务
     *
     * @param task
     */
    public void addDeltaTask(PeriodTask task) {
        this.deltaTaskList.add(task);
    }

    /**
     * 增加作业
     *
     * @param job
     */
    public void addJob(PeriodJob job) {
        this.latestJobMap.put(job.getJobCode(), job);
    }

    /**
     * 增加任务
     *
     * @param task
     */
    public void addTask(PeriodTask task) {
        this.latestTaskMap.put(task.getTaskCode(), task);
    }

    /**
     * 添加作业list
     */
    public synchronized void addJobTaskMap(Map<String, PeriodJob> latestJobMap, Map<String, PeriodTask> latestTaskMap) {
        if (showMemoryList) {
            //  循环打印  对比
            MapHelper.compareJobMap(latestJobMap, this.latestJobMap);
            MapHelper.compareTaskMap(latestTaskMap, this.latestTaskMap);
        }
        this.clearJobTask();
        this.latestJobMap = latestJobMap;
        this.latestTaskMap = latestTaskMap;
    }

    /**
     * 清空作业
     */
    private void clearJobTask() {
        this.latestJobMap.clear();
        this.latestTaskMap.clear();
    }

    /**
     * 清空delta
     */
    private void clearDelta() {
        this.deltaJobList.clear();
        this.deltaTaskList.clear();
    }

    /**
     * 更新最新完成任务
     *
     * @param srcTask
     */
    public void updateLstTask(PeriodTask srcTask) {
        //更新任务
        latestTaskMap.put(srcTask.getTaskCode(), srcTask);
        //添加delta
        if (this.needAddDelta()) {
            log.info("最新完成队列全量--添加delta【needAddDelta】任务【{}】【{}】",srcTask.getTaskCode(),srcTask.getBizDate());
            this.addDeltaTask(srcTask);
        }
    }

    /**
     * 更新最新完成作业
     *
     * @param srcJob
     */
    public void updateLstJob(PeriodJob srcJob) {
        //更新任务
        latestJobMap.put(srcJob.getJobCode(), srcJob);
        //添加delta
        if (this.needAddDelta()) {
            log.info("最新完成队列全量--添加delta【needAddDelta】作业【{}】【{}】",srcJob.getJobCode(),srcJob.getBizDate());
            this.addDeltaJob(srcJob);
        }
    }

    /**
     * 是否需要添加到delta
     *
     * @return
     */
    public boolean needAddDelta() {
        log.info("是否需要添加到delta【LstFinishManager.needAddDelta】");
        if (!Const.FREE_LOADER_MARK.equals(ToDoManager.getInstance().getMark())) {
            return true;
        }
        return false;
    }

    public void setMark(String mark){
        writeLock.lock();
        try {
            this.mark = mark;
        }finally {
            writeLock.unlock();
        }
    }

    public String getMark() {
        readLock.lock();
        try {
            return mark;
        }finally {
            readLock.unlock();
        }
    }

    public boolean loaderMark(String loadMark) {
        boolean flag = false;
        readLock.lock();
        if (StrUtil.equalsAnyIgnoreCase(Const.FREE_LST_FNS_MARK, mark)) {
            readLock.unlock();
            writeLock.lock();
            try {
                if (StrUtil.equalsAnyIgnoreCase(Const.FREE_LST_FNS_MARK, mark)) {
                    this.mark = loadMark;
                    flag = true;
                }
                readLock.lock();
            } finally {
                writeLock.unlock();
            }
        }
        try {
            return flag;
        } finally {
            readLock.unlock();
        }
    }

    public List<PeriodJob> getDeltaJobList(){
        return deltaJobList;
    }

    public void setCurQryTs(String curQryTs) {
        this.curQryTs = curQryTs;
    }

    public Map<String, PeriodJob> getLatestJobMap(){
        return latestJobMap;
    }

    public Map<String, PeriodTask> getLatestTaskMap() {
        return latestTaskMap;
    }

    public boolean isContainsJob(String jobCode) {
        return this.getJob(jobCode) == null ? false : true;
    }

    public boolean isContainsTask(String taskCode) {
        return this.getTask(taskCode) == null ? false : true;
    }
}
